import mui from './mui'
(function ($, window, document) {
  var STATE_BEFORECHANGEOFFSET = 'beforeChangeOffset'
  var STATE_AFTERCHANGEOFFSET = 'afterChangeOffset'

  var EVENT_PULLSTART = 'pullstart'
  var EVENT_PULLING = 'pulling'
  var EVENT_BEFORECHANGEOFFSET = STATE_BEFORECHANGEOFFSET
  var EVENT_AFTERCHANGEOFFSET = STATE_AFTERCHANGEOFFSET
  var EVENT_DRAGENDAFTERCHANGEOFFSET = 'dragEndAfterChangeOffset'

  var CLASS_TRANSITIONING = $.className('transitioning')
  var CLASS_PULL_TOP_TIPS = $.className('pull-top-tips')
  var CLASS_PULL_BOTTOM_TIPS = $.className('pull-bottom-tips')
  var CLASS_PULL_LOADING = $.className('pull-loading')
  var CLASS_SCROLL = $.className('scroll')

  var CLASS_PULL_TOP_ARROW = $.className('pull-loading') + ' ' + $.className('icon') + ' ' + $.className('icon-pulldown')
  var CLASS_PULL_TOP_ARROW_REVERSE = CLASS_PULL_TOP_ARROW + ' ' + $.className('reverse')
  var CLASS_PULL_TOP_SPINNER = $.className('pull-loading') + ' ' + $.className('spinner')
  var CLASS_HIDDEN = $.className('hidden')

  var SELECTOR_PULL_LOADING = '.' + CLASS_PULL_LOADING
  $.PullToRefresh = $.Class.extend({
    init: function (element, options) {
      this.element = element
      this.options = $.extend(true, {
        down: {
          height: 75,
          callback: false,
        },
        up: {
          auto: false,
          offset: 100, //距离底部高度(到达该高度即触发)
          show: true,
          contentinit: '上拉显示更多',
          contentdown: '上拉显示更多',
          contentrefresh: '正在加载...',
          contentnomore: '没有更多数据了',
          callback: false
        },
        preventDefaultException: {
          tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/
        }
      }, options)
      this.stopped = this.isNeedRefresh = this.isDragging = false
      this.state = STATE_BEFORECHANGEOFFSET
      this.isInScroll = this.element.classList.contains(CLASS_SCROLL)
      this.initPullUpTips()

      this.initEvent()
    },
    _preventDefaultException: function (el, exceptions) {
      for (var i in exceptions) {
        if (exceptions[i].test(el[i])) {
          return true
        }
      }
      return false
    },
    initEvent: function () {
      if ($.isFunction(this.options.down.callback)) {
        this.element.addEventListener($.EVENT_START, this)
        this.element.addEventListener('drag', this)
        this.element.addEventListener('dragend', this)
      }
      if (this.pullUpTips) {
        this.element.addEventListener('dragup', this)
        if (this.isInScroll) {
          this.element.addEventListener('scrollbottom', this)
        } else {
          window.addEventListener('scroll', this)
        }
      }
    },
    handleEvent: function (e) {
      switch (e.type) {
        case $.EVENT_START:
          this.isInScroll && this._canPullDown() && e.target && !this._preventDefaultException(e.target, this.options.preventDefaultException) && e.preventDefault()
          break
        case 'drag':
          this._drag(e)
          break
        case 'dragend':
          this._dragend(e)
          break
        case 'webkitTransitionEnd':
          this._transitionEnd(e)
          break
        case 'dragup':
        case 'scroll':
          this._dragup(e)
          break
        case 'scrollbottom':
          if (e.target === this.element) {
            this.pullUpLoading(e)
          }
          break
      }
    },
    initPullDownTips: function () {
      var self = this
      if ($.isFunction(self.options.down.callback)) {
        self.pullDownTips = (function () {
          var element = document.querySelector('.' + CLASS_PULL_TOP_TIPS)
          if (element) {
            element.parentNode.removeChild(element)
          }
          if (!element) {
            element = document.createElement('div')
            element.classList.add(CLASS_PULL_TOP_TIPS)
            element.innerHTML = '<div class="mui-pull-top-wrapper"><span class="mui-pull-loading mui-icon mui-icon-pulldown"></span></div>'
            element.addEventListener('webkitTransitionEnd', self)
          }
          self.pullDownTipsIcon = element.querySelector(SELECTOR_PULL_LOADING)
          document.body.appendChild(element)
          return element
        }())
      }
    },
    initPullUpTips: function () {
      var self = this
      if ($.isFunction(self.options.up.callback)) {
        self.pullUpTips = (function () {
          var element = self.element.querySelector('.' + CLASS_PULL_BOTTOM_TIPS)
          if (!element) {
            element = document.createElement('div')
            element.classList.add(CLASS_PULL_BOTTOM_TIPS)
            if (!self.options.up.show) {
              element.classList.add(CLASS_HIDDEN)
            }
            element.innerHTML = '<div class="mui-pull-bottom-wrapper"><span class="mui-pull-loading">' + self.options.up.contentinit + '</span></div>'
            self.element.appendChild(element)
          }
          self.pullUpTipsIcon = element.querySelector(SELECTOR_PULL_LOADING)
          return element
        }())
      }
    },
    _transitionEnd: function (e) {
      if (e.target === this.pullDownTips && this.removing) {
        this.removePullDownTips()
      }
    },
    _dragup: function (e) {
      var self = this
      if (self.loading) {
        return
      }
      if (e && e.detail && $.gestures.session.drag) {
        self.isDraggingUp = true
      } else {
        if (!self.isDraggingUp) { //scroll event
          return
        }
      }
      if (!self.isDragging) {
        if (self._canPullUp()) {
          self.pullUpLoading(e)
        }
      }
    },
    _canPullUp: function () {
      if (this.removing) {
        return false
      }
      if (this.isInScroll) {
        var scrollId = this.element.parentNode.getAttribute('data-scroll')
        if (scrollId) {
          var scrollApi = $.data[scrollId]
          return scrollApi.y === scrollApi.maxScrollY
        }
      }
      return window.pageYOffset + window.innerHeight + this.options.up.offset >= document.documentElement.scrollHeight
    },
    _canPullDown: function () {
      if (this.removing) {
        return false
      }
      if (this.isInScroll) {
        var scrollId = this.element.parentNode.getAttribute('data-scroll')
        if (scrollId) {
          var scrollApi = $.data[scrollId]
          return scrollApi.y === 0
        }
      }
      return document.body.scrollTop === 0
    },
    _drag: function (e) {
      if (this.loading || this.stopped) {
        e.stopPropagation()
        e.detail.gesture.preventDefault()
        return
      }
      var detail = e.detail
      if (!this.isDragging) {
        if (detail.direction === 'down' && this._canPullDown()) {
          if (document.querySelector('.' + CLASS_PULL_TOP_TIPS)) {
            e.stopPropagation()
            e.detail.gesture.preventDefault()
            return
          }
          this.isDragging = true
          this.removing = false
          this.startDeltaY = detail.deltaY
          $.gestures.session.lockDirection = true //锁定方向
          $.gestures.session.startDirection = detail.direction
          this._pullStart(this.startDeltaY)
        }
      }
      if (this.isDragging) {
        e.stopPropagation()
        e.detail.gesture.preventDefault()
        var deltaY = detail.deltaY - this.startDeltaY
        deltaY = Math.min(deltaY, 1.5 * this.options.down.height)
        this.deltaY = deltaY
        this._pulling(deltaY)
        var state = deltaY > this.options.down.height ? STATE_AFTERCHANGEOFFSET : STATE_BEFORECHANGEOFFSET
        if (this.state !== state) {
          this.state = state
          if (this.state === STATE_AFTERCHANGEOFFSET) {
            this.removing = false
            this.isNeedRefresh = true
          } else {
            this.removing = true
            this.isNeedRefresh = false
          }
          this['_' + state](deltaY)
        }
        if ($.os.ios && parseFloat($.os.version) >= 8) {
          var clientY = detail.gesture.touches[0].clientY
          if ((clientY + 10) > window.innerHeight || clientY < 10) {
            this._dragend(e)
            return
          }
        }
      }
    },
    _dragend: function (e) {
      var self = this
      if (self.isDragging) {
        self.isDragging = false
        self._dragEndAfterChangeOffset(self.isNeedRefresh)
      }
      if (self.isPullingUp) {
        if (self.pullingUpTimeout) {
          clearTimeout(self.pullingUpTimeout)
        }
        self.pullingUpTimeout = setTimeout(function () {
          self.isPullingUp = false
        }, 1000)
      }
    },
    _pullStart: function (startDeltaY) {
      this.pullStart(startDeltaY)
      $.trigger(this.element, EVENT_PULLSTART, {
        api: this,
        startDeltaY: startDeltaY
      })
    },
    _pulling: function (deltaY) {
      this.pulling(deltaY)
      $.trigger(this.element, EVENT_PULLING, {
        api: this,
        deltaY: deltaY
      })
    },
    _beforeChangeOffset: function (deltaY) {
      this.beforeChangeOffset(deltaY)
      $.trigger(this.element, EVENT_BEFORECHANGEOFFSET, {
        api: this,
        deltaY: deltaY
      })
    },
    _afterChangeOffset: function (deltaY) {
      this.afterChangeOffset(deltaY)
      $.trigger(this.element, EVENT_AFTERCHANGEOFFSET, {
        api: this,
        deltaY: deltaY
      })
    },
    _dragEndAfterChangeOffset: function (isNeedRefresh) {
      this.dragEndAfterChangeOffset(isNeedRefresh)
      $.trigger(this.element, EVENT_DRAGENDAFTERCHANGEOFFSET, {
        api: this,
        isNeedRefresh: isNeedRefresh
      })
    },
    removePullDownTips: function () {
      if (this.pullDownTips) {
        try {
          this.pullDownTips.parentNode && this.pullDownTips.parentNode.removeChild(this.pullDownTips)
          this.pullDownTips = null
          this.removing = false
        } catch (e) {}
      }
    },
    pullStart: function (startDeltaY) {
      this.initPullDownTips(startDeltaY)
    },
    pulling: function (deltaY) {
      this.pullDownTips.style.webkitTransform = 'translate3d(0,' + deltaY + 'px,0)'
    },
    beforeChangeOffset: function (deltaY) {
      this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW
    },
    afterChangeOffset: function (deltaY) {
      this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW_REVERSE
    },
    dragEndAfterChangeOffset: function (isNeedRefresh) {
      if (isNeedRefresh) {
        this.pullDownTipsIcon.className = CLASS_PULL_TOP_SPINNER
        this.pullDownLoading()
      } else {
        this.pullDownTipsIcon.className = CLASS_PULL_TOP_ARROW
        this.endPullDownToRefresh()
      }
    },
    pullDownLoading: function () {
      if (this.loading) {
        return
      }
      if (!this.pullDownTips) {
        this.initPullDownTips()
        this.dragEndAfterChangeOffset(true)
        return
      }
      this.loading = true
      this.pullDownTips.classList.add(CLASS_TRANSITIONING)
      this.pullDownTips.style.webkitTransform = 'translate3d(0,' + this.options.down.height + 'px,0)'
      this.options.down.callback.apply(this)
    },
    pullUpLoading: function (e) {
      if (this.loading || this.finished) {
        return
      }
      this.loading = true
      this.isDraggingUp = false
      this.pullUpTips.classList.remove(CLASS_HIDDEN)
      e && e.detail && e.detail.gesture && e.detail.gesture.preventDefault()
      this.pullUpTipsIcon.innerHTML = this.options.up.contentrefresh
      this.options.up.callback.apply(this)
    },
    endPullDownToRefresh: function () {
      this.loading = false
      this.pullUpTips && this.pullUpTips.classList.remove(CLASS_HIDDEN)
      this.pullDownTips.classList.add(CLASS_TRANSITIONING)
      this.pullDownTips.style.webkitTransform = 'translate3d(0,0,0)'
      if (this.deltaY <= 0) {
        this.removePullDownTips()
      } else {
        this.removing = true
      }
      if (this.isInScroll) {
        $(this.element.parentNode).scroll().refresh()
      }
    },
    endPullUpToRefresh: function (finished) {
      if (finished) {
        this.finished = true
        this.pullUpTipsIcon.innerHTML = this.options.up.contentnomore
        this.element.removeEventListener('dragup', this)
        window.removeEventListener('scroll', this)
      } else {
        this.pullUpTipsIcon.innerHTML = this.options.up.contentdown
      }
      this.loading = false
      if (this.isInScroll) {
        $(this.element.parentNode).scroll().refresh()
      }
    },
    setStopped: function (stopped) {
      if (stopped != this.stopped) {
        this.stopped = stopped
        this.pullUpTips && this.pullUpTips.classList[stopped ? 'add' : 'remove'](CLASS_HIDDEN)
      }
    },
    refresh: function (isReset) {
      if (isReset && this.finished && this.pullUpTipsIcon) {
        this.pullUpTipsIcon.innerHTML = this.options.up.contentdown
        this.element.addEventListener('dragup', this)
        window.addEventListener('scroll', this)
        this.finished = false
      }
    }
  })
  $.fn.pullToRefresh = function (options) {
    var pullRefreshApis = []
    options = options || {}
    this.each(function () {
      var self = this
      var pullRefreshApi = null
      var id = self.getAttribute('data-pullToRefresh')
      if (!id) {
        id = ++$.uuid
        $.data[id] = pullRefreshApi = new $.PullToRefresh(self, options)
        self.setAttribute('data-pullToRefresh', id)
      } else {
        pullRefreshApi = $.data[id]
      }
      if (options.up && options.up.auto) { //如果设置了auto，则自动上拉一次
        pullRefreshApi.pullUpLoading()
      }
      pullRefreshApis.push(pullRefreshApi)
    })
    return pullRefreshApis.length === 1 ? pullRefreshApis[0] : pullRefreshApis
  }
})(mui, window, document)
